/*
 * ProtocolHandler.cs
 * 
 * Microsoft Office SharePoint Server Managed Protocol Handler
 * 
 * Author: John Kozell (johnkoz@microsoft.com)
 *         Microsoft Coroporation
 * 
 * This code is an adaptation from, and interpretation of, the original SPS 2001 SDK
 * and has been updated with available information at the time of writing.
 * Not all the delcarions and alogrithms have been verified and tested
 * and there is no guarantee that they are accurate or valid, now or in the future.
 * 
 * The .NET Managed COM Interop approach contained herin was NOT created in cooperation
 * with the Microsoft SharePoint product group and has not been officially endorced
 * by Microsoft and therefore is not supported. The user of this code assumes any
 * and all risk with its use and should proceed with discression.
 * 
 * THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 */

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;


namespace MOSSPH
{
    [ComVisible(true)]
    [ClassInterfaceAttribute(ClassInterfaceType.None), AutomationProxy(false)] 
	public class ProtocolHandler : ISearchProtocol
	{
		public static readonly string PERF_CATEGORY = ContentEnumerator.ProtocolPrefix;
		public static readonly string PERF_COUNT_ACCESSORS = "Current Accessors";

		private PerformanceCounter m_PCAccessors = null;
        private string m_UserAgent = string.Empty;

		#region ISearchProtocol Members

        public ISearchProtocolReturnCodes Init(ref TIMEOUT_INFO pTimeoutInfo, IProtocolHandlerSite pProtocolHandlerSite, ref PROXY_INFO pProxyInfo)
		{
			DateTime enterTime = Logging.Enter(typeof(ProtocolHandler), "Init");
            ISearchProtocolReturnCodes hr = (ISearchProtocolReturnCodes)SCODE.S_OK;

//System.Threading.Thread.Sleep(30000); // Time to attach the debugger while troubleshooting.

            m_UserAgent = pProxyInfo.pcwszUserAgent;
            Logging.Information(string.Format("Initializing protocol handler. Agent: {0}", m_UserAgent));

            // Set security principal for this app domain.
			AppDomain.CurrentDomain.SetPrincipalPolicy(System.Security.Principal.PrincipalPolicy.WindowsPrincipal);

			if (!PerformanceCounterCategory.Exists(PERF_CATEGORY))
			{
				CounterCreationDataCollection counters = new CounterCreationDataCollection();

				CounterCreationData accessors = new CounterCreationData("Current Accessors", "Number of active Accessors", PerformanceCounterType.NumberOfItems32);
				counters.Add(accessors);

				PerformanceCounterCategory.Create(PERF_CATEGORY, "MOSSPH Protocol Handler", PerformanceCounterCategoryType.SingleInstance, counters);
			}

			m_PCAccessors = new PerformanceCounter(PERF_CATEGORY, PERF_COUNT_ACCESSORS, false);
			m_PCAccessors.RawValue = 0;

			Logging.Leave(typeof(ProtocolHandler), "Init", null, enterTime);
            return hr;
		}


        public ISearchProtocolReturnCodes CreateAccessor(string pcwszURL, ref AUTHENTICATION_INFO pAuthenticationInfo, ref INCREMENTAL_ACCESS_INFO pIncrementalAccessInfo, ref ITEM_INFO pItemInfo, out IUrlAccessor ppAccessor)
		{
			DateTime enterTime = Logging.Enter(typeof(ProtocolHandler), "CreateAccessor", pcwszURL + " [" + 
				pIncrementalAccessInfo.ftLastModifiedTime.dwHighDateTime.ToString("X8") + pIncrementalAccessInfo.ftLastModifiedTime.dwLowDateTime.ToString("X8") + "]");
            ISearchProtocolReturnCodes hr = (ISearchProtocolReturnCodes)SCODE.S_OK;
			IUrlAccessor pAccessor = null;

			// pcwszURL will be in the form: PPPPP://DDDDD/.....
			// Where:
			//     PPPPP is the protocol this handler recognises (stored in "ProtocolPrefix").
			//     DDDDD is some domain name.
			// Note: 3 slashes are required, even if nothing follows the 3rd.

			// Build an enumerator for the containers and items.
			IContentEnumerator pContent = new ContentEnumerator();
			if (pContent != null)
			{
                hr = (ISearchProtocolReturnCodes)pContent.Init(pcwszURL, m_UserAgent);
                if (hr == (ISearchProtocolReturnCodes)SCODE.S_OK)
                {
                    // See if the page has chanced since last crawl.
                    FILETIME ftModified = COMInterop.DateTimeToFileTime(pContent.LastModified);
                    Logging.Trace(string.Format("Last modified: {0} ({1:D8}{2:D8})", pContent.LastModified, ftModified.dwHighDateTime, ftModified.dwLowDateTime));
                    if (!COMInterop.CompareFILETIME(ftModified, pIncrementalAccessInfo.ftLastModifiedTime))
                    {
                        Accessor accessor = new Accessor();

                        if (accessor.Init(pContent) == (HRESULT)SCODE.S_OK)
                        {
                            pAccessor = (IUrlAccessor)accessor;
                            m_PCAccessors.Increment();
                        }
                        else
                        {
                            accessor.Close();
                        }
                        accessor = null;
                    }
                    else
                    {
                        Logging.Trace("Page not changed: " + pcwszURL);
                        hr = ISearchProtocolReturnCodes.PRTH_S_NOT_MODIFIED;
                    }
                    pContent = null;
                }
			}

			// Return accessor resource to the filter daemon.
			ppAccessor = pAccessor;

			Logging.Leave(typeof(ProtocolHandler), "CreateAccessor", hr.ToString(), enterTime);
            return hr;
		}

        public ISearchProtocolReturnCodes CloseAccessor(IUrlAccessor pAccessor)
		{
            Logging.Enter(typeof(ProtocolHandler), "CloseAccessor");
            ISearchProtocolReturnCodes hr = (ISearchProtocolReturnCodes)SCODE.S_OK;
            
            Accessor accessor = pAccessor as Accessor;
			if (accessor != null)
			{
				Logging.Information("Closing Accessor: " + accessor.Content.Url);
				accessor.Close();
				m_PCAccessors.Decrement();
			}

			Logging.Leave(typeof(ProtocolHandler), "CloseAccessor");
            return hr;
		}

        public ISearchProtocolReturnCodes ShutDown()
		{
			Logging.Called(typeof(ProtocolHandler), "ShutDown");

			m_PCAccessors = null;

            return (ISearchProtocolReturnCodes)SCODE.S_OK;
		}

		#endregion
	}
}
